#include "Rp2R1Loader.h"
#include "Rp2ColorRGBA.h"
#include "Rp2IndexBuffer.h"
#include "Rp2VertexBuffer.h"
#include "Rp2Attributes.h"
#include "Rp2TriMesh.h"
#include "Rp2Graphics.h"
#include "Rp2CullState.h"

// Disable Microsoft warning about unsafe functions (security).
#pragma warning(disable:4996)

using namespace Rp2;

//---------------------------------------------------------------------------------------------------
R1Loader::R1Loader()
{
}
//---------------------------------------------------------------------------------------------------
R1Loader::~R1Loader()
{
}
//---------------------------------------------------------------------------------------------------
bool R1Loader::Load(std::string& rkFilename, Node& rkNode)
{

	const char* filePath = System::GetPath(rkFilename.c_str(), System::SM_READ);
	FILE* pkFile;
	fopen_s(&pkFile, filePath, "rb");
	if (!pkFile)
	{
		System::MsgBoxPrintf(TC("R1Loader::Load"),
			TC("Can not open file : %s"), rkFilename.c_str());
		assert(false);
		return 0;
	}	

	int mesh_num = 0;
	// get mesh number
	System::Read4le(pkFile, 1, &mesh_num);

 	// read data in each mesh
 	for (int i = 0; i < mesh_num; i++)
 	{
		int iTmp;

		char m_flag;
		// material
		int mat_name_length;
		char* mat_name;
		int mat_type;
		ColorRGB kE, kA, kD, kS;
		float alpha, shininess;
		// texture
		int isMultiTextured, tCoord_sets, tex_num;
		std::vector<std::string> tex_names;
		int tex_operationType, tex_addressMode_u, tex_addressMode_v;
		// buffer
		int v_num, v_channel, vba_channel, i_num;
		VertexBuffer* pkVBuffer;
		IndexBuffer* pkIBuffer;
		// animation
		SkinningEffect* pkSkinEffect;
		int iAnim_index;
		float fAnim_Length_sec; // animation length in second
		float fAnim_SampleRate; 
		int iJointNum;
		Skeleton* pkSkeleton;

		struct Joint 
		{
			int id;
			std::string name;
			int parentId;
			float pos[3];
			float qOrient[4];
		};
		Joint kJoint;


 		// mesh begin flag, must be 'm'
		System::Read1(pkFile,1, &m_flag);
		if (m_flag != 'm')
		{
			assert(false);
			return false;
		}

		// ============ material info ==============
		// material name
		System::Read4le(pkFile, 1, &mat_name_length);
 		mat_name = new char[mat_name_length];
		System::Read1(pkFile, mat_name_length, mat_name);

 		// material type
		System::Read4le(pkFile, 1, &mat_type);

 		// material color
		System::Read4le(pkFile, 3, (void*)&kE);
		System::Read4le(pkFile, 3, (void*)&kA);
		System::Read4le(pkFile, 3, (void*)&kD);
		System::Read4le(pkFile, 3, (void*)&kS);
		System::Read4le(pkFile, 1, &alpha);
		System::Read4le(pkFile, 1, &shininess);

 		// ============ texture info ==============
 		// is multitexutred 
		System::Read4le(pkFile, 1, &isMultiTextured);

 		// texCoord sets quantity
		System::Read4le(pkFile, 1, &tCoord_sets);

		// texture number
		System::Read4le(pkFile, 1, &tex_num);

		for (int j = 0; j < tex_num; j++)
		{
			// texture path/name
			System::Read4le(pkFile, 1, &iTmp);
			char* acName = new char[iTmp];
			System::Read1(pkFile, iTmp, acName);
			std::string strName(acName, iTmp);
			tex_names.push_back(strName);
			if (acName)
			{
				delete acName;
			}
			// texture operation type
			System::Read4le(pkFile, 1, &tex_operationType);	

			// texture address mode
			System::Read4le(pkFile, 1, &tex_addressMode_u);
			System::Read4le(pkFile, 1, &tex_addressMode_v);
		}

		// ============ vertex buffer ==============
		// vertices number
		System::Read4le(pkFile, 1, &v_num);	
		// vertex channel
		System::Read4le(pkFile, 1, &v_channel);
		// vba channel
		System::Read4le(pkFile, 1, &vba_channel);
		// vertex buffer data
		// set attributes
		Attributes Attr;
		Attr.SetPositionChannels(3);
		Attr.SetBlendWeightChannels(vba_channel);
		Attr.SetNormalChannels(3);
		Attr.SetBlendIndicesChannels(vba_channel);	
		for (int j = 0; j < tCoord_sets; j++)
		{
			Attr.SetTCoordChannels(j, 2);
		}

		// construct buffer
		pkVBuffer = new VertexBuffer(Attr, v_num);
		float* vData = pkVBuffer->GetData();
		System::Read4le(pkFile, v_num * v_channel, vData);
	
		// ============ index buffer ==============
		System::Read4le(pkFile, 1, &i_num);
		pkIBuffer = new IndexBuffer(i_num);
		int* iData = pkIBuffer->GetData();
		System::Read4le(pkFile, i_num, iData);

		// ============ skeleton strcuture ==============
		if (vba_channel > 0)
		{
			pkSkeleton = new Skeleton;

			// joints number
			System::Read4le(pkFile, 1, &iJointNum);

			for (int j = 0; j < iJointNum; j++)
			{
				// joint id
				System::Read4le(pkFile, 1, &kJoint.id);

				// joint name length
				System::Read4le(pkFile, 1, &iTmp);

				// joint name
				char* acName = new char[iTmp];
				System::Read1(pkFile, iTmp, acName);
				kJoint.name = std::string(acName, iTmp);

				// parent id
				System::Read4le(pkFile, 1, &kJoint.parentId);

				// translate (relative to it's parent)
				Vector3f kT;
				System::Read4le(pkFile, 3, (float*)kT);
				// orientation (relative to it's parent)
				Quaternionf kQ;
				System::Read4le(pkFile, 4, (float*)kQ);

				// create bones
				Bone* pkBone = pkSkeleton->CreateBone(kJoint.name, kJoint.id, kJoint.parentId);
				pkBone->SetRelTranslate(kT);	
				pkBone->SetRelOrientation(kQ);
			}

			// construct the whole skeleton
			pkSkeleton->ConstructHierarchy();
		}

		// ============ skeleton animation ==============
		if (vba_channel > 0)
		{
			// create skin effect
			pkSkinEffect = new SkinningEffect("");
			pkSkinEffect->SetSkeleton(pkSkeleton);

			// attach skeleton keyframe controller
			SKController* pkCtrl = new SKController;
			pkSkinEffect->AttachController(pkCtrl);

			// animation index
			System::Read4le(pkFile, 1, &iAnim_index);

			// animation length in second
			System::Read4le(pkFile, 1, &fAnim_Length_sec);
			std::cout << "Anim length: " << fAnim_Length_sec << std::endl;

			// sample rate
			System::Read4le(pkFile, 1, &fAnim_SampleRate);
			std::cout << "Sample Rate: " << fAnim_SampleRate << std::endl;

			// set control attr
			pkCtrl->Repeat = (Controller::RepeatType)Controller::RT_WRAP;
			pkCtrl->MinTime = 0.0f;
			pkCtrl->MaxTime = fAnim_Length_sec;
			pkCtrl->Phase = 0.0;
			pkCtrl->Frequency = 2.0f;
			// calculate frames quantity
			int iFrames = 0;
			for (float t = 0.0f; t < fAnim_Length_sec; t+=fAnim_SampleRate)
			{
				++iFrames;
			}
			++iFrames;
			pkCtrl->FrameQuantity.push_back(iFrames);
			// set control times
			float* afTime = new float[iFrames];
			for (int j = 0; j < iFrames-1; j++)
			{
				afTime[j] = (float)j * fAnim_SampleRate;
			}
			afTime[iFrames-1] = fAnim_Length_sec;
			pkCtrl->Times = new FloatArray(iFrames, afTime);

			// allocate memory
			int iQuantity = iJointNum*iFrames;
			Vector3f* akTranslationData = new Vector3f[iQuantity];
			Quaternionf* akRotationData = new Quaternionf[iQuantity];
			float* afScaleData = new float[iQuantity];
			pkCtrl->TranslationData = new Vector3fArray(iQuantity, akTranslationData);
			pkCtrl->RotationData = new QuaternionfArray(iQuantity, akRotationData);
			pkCtrl->ScaleData = new FloatArray(iQuantity, afScaleData);

			// keyframes of each joint
			for (int j = 0; j < iJointNum; j++)
			{
				for (int k = 0; k < iFrames; k++)
				{
					// translate
					Vector3f kT(Vector3f::ZERO);
					pkCtrl->SetTranslate(j, k, kT);
					// rotation
					Quaternionf kQ;
					System::Read4le(pkFile, 4, (float*)kQ);
					pkCtrl->SetRotate(j, k, kQ);
	 				// scale
					float afScale[3];
	 				System::Read4le(pkFile, 3, afScale);
					pkCtrl->SetScale(j, k, afScale[0]);
				}
			}
		}

		// create keyframe controller

	
		// ============ update node ================
		// create mesh
		TriMesh* pkTrimesh = new TriMesh(pkVBuffer, pkIBuffer);
		// attach material state
		MaterialState* pkMatState = new MaterialState;
		pkMatState->Emissive = kE;
		pkMatState->Ambient = kA;
		pkMatState->Diffuse = kD;
		pkMatState->Specular = kS;
		pkMatState->Alpha = alpha;
		pkMatState->Shininess = shininess;
		pkTrimesh->AttachGlobalState(pkMatState);

		// attach effect
		// if textured, attach texture effect
		if (tex_num > 0)
		{
			if (vba_channel > 0)
			{
				// this model includes skeleton animation
				pkTrimesh->AttachEffect(new SkinningEffect(tex_names[0]));
			}
			else
			{
				// default lighting material effect with texture
				pkTrimesh->AttachEffect(new LightingMaterialEffect(tex_names[0]));
			}
		}
		else
		{
			//pkTrimesh->AttachEffect(new LightingMaterialEffect(""));
			//pkTrimesh->AttachEffect(new SkinningEffect(""));
			pkTrimesh->AttachEffect(pkSkinEffect);
		}
 		rkNode.AttachChild(pkTrimesh);

		// ============ for debugging ==============

// 		std::cout << "Mesh number: " << mesh_num << std::endl;
// 		std::cout << "Flag: " << m_flag << std::endl;
// 		std::cout << "Material name length: " << mat_name_length << std::endl;
// 		std::cout << "Material name: " << mat_name << std::endl;
// 		std::cout << "Material type enum: " << mat_type << std::endl;
// 		std::cout << "kE: " << kE.R() << ", " << kE.G() << ", " 
// 				  << kE.B() << std::endl; 
// 		std::cout << "kA: " << kA.R() << ", " << kA.G() << ", " 
// 				  << kA.B() << std::endl; 
// 		std::cout << "kD: " << kD.R() << ", " << kD.G() << ", " 
// 				  << kD.B() << std::endl; 
// 		std::cout << "kS: " << kS.R() << ", " << kS.G() << ", " 
// 				  << kS.B() << std::endl; 
// 		std::cout << "Alpha: " << alpha << std::endl;
// 		std::cout << "Shininess: " << shininess << std::endl;			
// 		std::cout << "IsMultiTextured: " << isMultiTextured << std::endl;			
//  		std::cout << "TexCoords sets quantity: " << tCoord_sets << std::endl;			
// 		std::cout << "Texture number: " << tex_num << std::endl;			
// 		for (int j = 0; j < tex_num; j++)
// 		{
// 			std::cout << "Tex name " << j << ": " << tex_names[j] << std::endl;
// 			std::cout << "Tex operation type: " << tex_operationType << std::endl;
// 			std::cout << "Tex address mode u: " << tex_addressMode_u << std::endl;
// 			std::cout << "Tex address mode v: " << tex_addressMode_u << std::endl;
// 		}			
// 		std::cout << "Vertex quantity: " <<  v_num << std::endl;
// 		std::cout << "Vertex channel: " <<  v_channel << std::endl;
// 		std::cout << "Vba channel: " << vba_channel << std::endl;
// 		std::cout << "VBuffer: v channle: " << pkVBuffer->GetAttributes().GetChannelQuantity() << std::endl;
// 		std::cout << "VBuffer: v quantity: " << pkVBuffer->GetVQuantity() << std::endl;
// 		std::cout << "VBuffer size: " << v_num * v_channel * 4 << std::endl;
// 		std::cout << "Index quantity: " << i_num << std::endl;
//  	pkVBuffer->PrintToFile("R1VBuffer.txt");	// output Vbuffer data for debugging	
//  	pkIBuffer->PrintToFile("R1IBuffer.txt");	// output Ibuffer data for debugging
// 		std::cout << "Finish =========== Mesh in Node: " << rkNode.GetQuantity() << std::endl;
	
	}// end for mesh

	fclose(pkFile);
	return true;
}
//---------------------------------------------------------------------------------------------------

//---------------------------------------------------------------------------------------------------